import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { minimatch } from 'minimatch';
import {
  Action,
  PermissionPolicy,
  ProjectRole,
  LcProjectMember,
  DocPermission,
} from '../types/auth.type';
import { LcProject } from '../types/editor.type';
import { HttpService } from '@nestjs/axios';

/**
 * 资源标识
 */
export interface ResourceIdentifier {
  projectId: string;
  path: string; // 文件路径，如：/src/modules/user-management/index.tsx
}

/**
 * 权限检查结果
 */
export interface PermissionCheckResult {
  allowed: boolean;
  reason?: string;
  matchedPolicy?: PermissionPolicy;
}

/**
 * 访问控制服务
 */
@Injectable()
export class AccessControlService {
  constructor(
    @InjectModel('project')
    private readonly projectModel: Model<LcProject>,
    private readonly httpService: HttpService,
  ) {}

  /**
   * 检查用户是否有权限对资源执行操作
   * 🆕 支持匿名用户访问（userId = 0）
   */
  async checkPermission(
    userId: number | null | undefined,
    resource: ResourceIdentifier,
    action: Action,
  ): Promise<PermissionCheckResult> {
    // 1. 获取项目
    const project = await this.projectModel.findById(resource.projectId);
    if (!project) {
      return {
        allowed: false,
        reason: '项目不存在',
      };
    }

    // 2. 处理匿名用户（userId 为 null/undefined 时统一设为 0）
    const anonymousUserId = userId || 0;

    // 3. 检查是否是项目所有者（匿名用户不可能是所有者）
    if (userId && project.userId === userId) {
      return { allowed: true };
    }

    // console.log('anonymousUserId: ', anonymousUserId);
    // console.log('project: ', project.publicIdentifier);
    // console.log('project.member: ', project.member);

    // 4. 检查项目成员权限（包括特殊的游客成员 userId: 0）
    let member = project.member?.find((m) => m.userId === anonymousUserId);

    // 🆕 如果已登录用户不是项目成员，但项目开启了公开访问，则使用游客权限
    if (!member && anonymousUserId !== 0) {
      const guestMember = project.member?.find((m) => m.userId === 0);
      if (guestMember) {
        console.log(
          `用户 ${anonymousUserId} 不是项目成员，但项目开启了公开访问，使用游客权限`,
        );
        member = guestMember;
      }
    }

    if (!member) {
      return {
        allowed: false,
        reason: anonymousUserId === 0 ? '项目未开启游客访问' : '您不是项目成员',
      };
    }

    // 🆕 5. 兼容旧权限系统：如果成员没有新角色，使用旧权限逻辑
    if (!member.role && !member.policies && member.documentPermission) {
      // 将 Action 映射到旧权限
      const requiredPermission = [
        'source:edit',
        'source:export',
        'create',
        'update',
        'delete',
        'grant',
      ].includes(action)
        ? 'edit'
        : 'view';

      const hasPermission =
        member.documentPermission === requiredPermission ||
        member.documentPermission === 'edit';

      return {
        allowed: hasPermission,
        reason: hasPermission ? undefined : `您没有${action}权限`,
      };
    }

    // 6. 获取用户的权限策略
    const policies = this.getUserPolicies(member);

    // 5. 按优先级排序
    policies.sort((a, b) => b.priority - a.priority);

    // 6. 检查每个策略
    for (const policy of policies) {
      // 检查路径是否匹配
      if (!this.matchPath(resource.path, policy.pathPattern)) {
        continue;
      }

      // 检查条件
      if (policy.conditions && !this.checkConditions(policy.conditions)) {
        continue;
      }

      // 检查是否过期
      if (policy.expiresAt && policy.expiresAt < Date.now()) {
        continue;
      }

      // 检查是否明确拒绝
      if (policy.deniedActions?.includes(action)) {
        return {
          allowed: false,
          reason: `操作被策略 "${policy.name}" 明确拒绝`,
          matchedPolicy: policy,
        };
      }

      // 检查是否允许
      if (policy.actions.includes(action)) {
        return {
          allowed: true,
          matchedPolicy: policy,
        };
      }
    }

    // 7. 默认拒绝
    return {
      allowed: false,
      reason: '没有匹配的权限策略',
    };
  }

  /**
   * 批量检查权限
   */
  async checkPermissions(
    userId: number | null | undefined,
    resource: ResourceIdentifier,
    actions: Action[],
  ): Promise<Record<Action, boolean>> {
    const results: Record<Action, boolean> = {} as any;

    for (const action of actions) {
      const result = await this.checkPermission(userId, resource, action);
      results[action] = result.allowed;
    }

    return results;
  }

  /**
   * 获取用户在项目中的所有权限
   */
  async getUserPermissions(
    userId: number | null | undefined,
    projectId: string,
    filePath: string,
  ): Promise<Action[]> {
    const allActions: Action[] = [
      'view',
      'run',
      'debug',
      'source:view',
      'source:edit',
      'source:export',
      'create',
      'update',
      'delete',
      'move',
      'copy',
      'publish',
      'unpublish',
      'grant',
      'revoke',
    ];

    const results = await this.checkPermissions(
      userId,
      { projectId, path: filePath },
      allActions,
    );

    return allActions.filter((action) => results[action]);
  }

  /**
   * 🆕 检查项目是否支持公开访问
   * 通过检查是否有游客成员（userId: 0）来判断
   */
  async checkPublicAccess(projectId: string): Promise<boolean> {
    const project = await this.projectModel.findById(projectId);
    if (!project) {
      return false;
    }

    // 检查是否有游客成员
    const hasGuestMember = project.member?.some((m) => m.userId === 0);
    return !!hasGuestMember;
  }

  /**
   * 🆕 配置项目的公开访问（统一接口）
   * @param projectId 项目ID
   * @param config 配置参数
   * @param operatorId 操作者ID
   */
  async configurePublicAccess(
    projectId: string,
    config: {
      enabled: boolean;
      publicIdentifier?: string;
      policies?: PermissionPolicy[];
      customDomain?: {
        domain: string;
        forceHttps?: boolean;
      };
    },
    operatorId?: number,
  ): Promise<any> {
    const project = await this.projectModel.findById(projectId);
    if (!project) {
      throw new Error('项目不存在');
    }

    const now = Date.now();
    const updateData: any = {};
    let identifier: string | undefined;

    if (config.enabled) {
      // 启用公开访问

      // 1. 设置标识符 - 智能逻辑
      if (config.publicIdentifier) {
        // 如果用户明确指定了标识符，使用用户指定的
        identifier = config.publicIdentifier;
        updateData.publicIdentifier = identifier; // 强制更新为用户指定的值
      } else if (project.publicIdentifier) {
        // 如果数据库中已有标识符且用户没有指定，复用已有的
        identifier = project.publicIdentifier;
        // 不需要更新数据库，保持原值
      } else {
        // 数据库中没有且用户没有指定，生成新的
        identifier = this.generateShortIdentifier();
        updateData.publicIdentifier = identifier;
      }
      updateData.publicEnabledAt = now;
      updateData.publicEnabledBy = operatorId;

      // 2. 配置自定义域名
      if (config.customDomain) {
        updateData.customDomain = {
          domain: config.customDomain.domain,
          status: 'pending', // 初始状态为待验证
          dnsStatus: {
            cnameValid: false,
            lastChecked: now,
          },
          ssl: {
            status: 'none',
            autoRenew: true,
          },
          createdAt: now,
          updatedAt: now,
          createdBy: operatorId,
          forceHttps: config.customDomain.forceHttps !== false, // 默认为 true
        };
      }

      // 3. 配置游客权限（在 member 数组中）
      const guestMember: LcProjectMember = {
        userId: 0,
        role: 'guest',
        inviteAt: now,
        inviteBy: operatorId || project.userId,
        name: '游客',
        policies: config.policies || [],
      };

      // 更新或添加游客成员
      await this.projectModel
        .updateOne(
          {
            _id: projectId,
            'member.userId': 0,
          },
          {
            $set: {
              'member.$.inviteAt': now,
              'member.$.role': 'guest',
              'member.$.policies': config.policies || [],
              'member.$.inviteBy': operatorId || project.userId,
            },
          },
        )
        .then(async (result) => {
          if (result.modifiedCount === 0) {
            // 没有找到游客成员，添加新的
            await this.projectModel.updateOne(
              { _id: projectId },
              { $push: { member: guestMember } },
            );
          }
        });
    } else {
      // 禁用公开访问 - 保留 publicIdentifier 用于复用
      updateData.$unset = {
        publicEnabledAt: 1, // 只删除启用时间戳
        publicEnabledBy: 1, // 只删除启用者
        customDomain: 1, // 删除自定义域名配置
      };
      // 注意：不删除 publicIdentifier，以便重新启用时复用

      // 移除游客成员
      await this.projectModel.updateOne(
        { _id: projectId },
        { $pull: { member: { userId: 0 } } },
      );
    }

    // 更新项目信息
    const result = await this.projectModel.updateOne(
      { _id: projectId },
      { ...updateData },
    );

    // 重新获取项目以确保返回正确的标识符
    const updatedProject = await this.projectModel.findById(projectId);

    // 🆕 通知编辑器更新项目数据
    if (result.modifiedCount > 0 && updatedProject) {
      await this.notifyEditorUpdate(projectId, {
        publicEnabledAt: updatedProject.publicEnabledAt,
        publicEnabledBy: updatedProject.publicEnabledBy,
        publicIdentifier: updatedProject.publicIdentifier,
        customDomain: updatedProject.customDomain,
      });
    }

    return {
      success: result.modifiedCount > 0,
      publicIdentifier: config.enabled ? updatedProject.publicIdentifier : null,
      customDomain: config.enabled ? config.customDomain?.domain : null,
    };
  }

  /**
   * 🆕 通知编辑器更新项目数据
   */
  private async notifyEditorUpdate(projectId: string, updateData: any) {
    try {
      // 构造需要通知编辑器更新的数据，对应前端的updateProject调用
      const editorUpdateData = {
        publicEnabledAt: updateData.publicEnabledAt,
        publicEnabledBy: updateData.publicEnabledBy,
        publicIdentifier: updateData.publicIdentifier,
        customDomain: updateData.customDomain,
        // 其他需要更新的字段...
      };

      console.log('🔔 通知编辑器更新项目数据:', {
        projectId,
        updateData: editorUpdateData,
        localUpdate: true,
      });

      // TODO: 请根据你的实际情况实现通知逻辑
      // 这里提供几种可能的通知方式：

      // 方式1: 通过WebSocket通知编辑器
      // if (this.socketGateway) {
      //   this.socketGateway.server.emit('project-update', {
      //     projectId,
      //     updateData: editorUpdateData,
      //     localUpdate: true
      //   });
      // }

      // 方式2: 通过HTTP API通知编辑器
      // await this.httpService.post('/api/editor/project/update', {
      //   projectId,
      //   updateData: editorUpdateData,
      //   localUpdate: true
      // }).toPromise();

      // 方式3: 通过事件系统通知
      // this.eventEmitter.emit('project.updated', {
      //   projectId,
      //   updateData: editorUpdateData,
      //   localUpdate: true
      // });

      // 方式4: 通过Redis或其他消息队列通知
      // await this.redisService.publish('project-updates', JSON.stringify({
      //   projectId,
      //   updateData: editorUpdateData,
      //   localUpdate: true
      // }));

      console.log('✅ 编辑器通知已发送（请根据实际需求实现具体通知逻辑）');
    } catch (error) {
      console.error('❌ 通知编辑器更新失败:', error);
      // 不抛出错误，避免影响主要功能
    }
  }

  /**
   * 🆕 获取项目的公开访问配置
   */
  async getPublicAccessConfig(projectId: string) {
    const project = await this.projectModel.findById(projectId);
    if (!project) {
      return null;
    }

    const guestMember = project.member?.find((m) => m.userId === 0);

    return {
      enabled: !!project.publicEnabledAt, // 通过启用时间戳判断是否启用
      publicIdentifier: project.publicIdentifier,
      publicEnabledAt: project.publicEnabledAt,
      publicEnabledBy: project.publicEnabledBy,
      customDomain: project.customDomain || undefined,
      guestPolicies: guestMember?.policies || [],
    };
  }

  /**
   * 🆕 根据标识符查找项目
   */
  async getProjectByIdentifier(identifier: string) {
    const project = await this.projectModel.findOne({
      publicIdentifier: identifier,
    });

    if (!project || !project.publicIdentifier) {
      return null;
    }

    return project;
  }

  /**
   * 🆕 生成短标识符
   */
  private generateShortIdentifier(): string {
    const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
    let result = '';
    for (let i = 0; i < 6; i++) {
      result += chars.charAt(Math.floor(Math.random() * chars.length));
    }
    return result;
  }

  /**
   * 获取用户的权限策略
   * 🆕 支持角色策略与自定义策略叠加
   */
  private getUserPolicies(member: LcProjectMember): PermissionPolicy[] {
    const policies: PermissionPolicy[] = [];

    // 1. 始终添加角色的默认策略作为基础权限
    const rolePolicies = this.getRoleDefaultPolicies(member.role || 'guest');
    policies.push(...rolePolicies);

    // 2. 如果有自定义策略，叠加到角色策略之上（更高优先级）
    if (member.policies && member.policies.length > 0) {
      policies.push(...member.policies);
    }

    return policies;
  }

  /**
   * 获取角色的默认策略
   */
  private getRoleDefaultPolicies(role: ProjectRole): PermissionPolicy[] {
    const now = Date.now();

    const ROLE_POLICIES: Record<ProjectRole, PermissionPolicy[]> = {
      owner: [
        {
          id: 'owner-all',
          name: '所有者 - 完全访问',
          pathPattern: '/**',
          actions: [
            'view',
            'source:view',
            'source:edit',
            'source:export',
            'create',
            'update',
            'delete',
            'move',
            'copy',
            'publish',
            'unpublish',
            'grant',
            'revoke',
            'run',
            'debug',
          ],
          priority: 1000,
          createdBy: 0,
          createdAt: now,
        },
      ],

      maintainer: [
        {
          id: 'maintainer-all',
          name: '维护者 - 几乎完全访问',
          pathPattern: '/**',
          actions: [
            'view',
            'source:view',
            'source:edit',
            'source:export',
            'create',
            'update',
            'delete',
            'move',
            'copy',
            'publish',
            'unpublish',
            'run',
            'debug',
          ],
          priority: 900,
          createdBy: 0,
          createdAt: now,
        },
      ],

      developer: [
        {
          id: 'developer-all',
          name: '开发者 - 完全访问',
          pathPattern: '/**',
          actions: [
            'view',
            'source:view',
            'source:edit',
            'create',
            'update',
            'delete',
            'move',
            'copy',
            'run',
            'debug',
          ],
          priority: 800,
          createdBy: 0,
          createdAt: now,
        },
      ],

      'frontend-dev': [
        {
          id: 'frontend-pages',
          name: '前端开发者 - 页面完全访问',
          pathPattern: '/src/pages/**',
          actions: [
            'view',
            'source:view',
            'source:edit',
            'create',
            'update',
            'delete',
            'run',
            'debug',
          ],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
        {
          id: 'frontend-components',
          name: '前端开发者 - 组件完全访问',
          pathPattern: '/src/components/**',
          actions: [
            'view',
            'source:view',
            'source:edit',
            'create',
            'update',
            'delete',
            'run',
          ],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
        {
          id: 'frontend-styles',
          name: '前端开发者 - 样式完全访问',
          pathPattern: '**/*.{css,scss,less,sass}',
          actions: ['view', 'source:view', 'source:edit'],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
        {
          id: 'frontend-api-readonly',
          name: '前端开发者 - API只读',
          pathPattern: '/src/api/**',
          actions: ['view', 'run'],
          deniedActions: ['source:view', 'source:edit'],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
      ],

      'backend-dev': [
        {
          id: 'backend-api',
          name: '后端开发者 - API完全访问',
          pathPattern: '/src/api/**',
          actions: [
            'view',
            'source:view',
            'source:edit',
            'create',
            'update',
            'delete',
            'run',
            'debug',
          ],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
        {
          id: 'backend-services',
          name: '后端开发者 - 服务完全访问',
          pathPattern: '/src/services/**',
          actions: [
            'view',
            'source:view',
            'source:edit',
            'create',
            'update',
            'delete',
          ],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
        {
          id: 'backend-database',
          name: '后端开发者 - 数据库完全访问',
          pathPattern: '/src/database/**',
          actions: ['view', 'source:view', 'source:edit', 'update'],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
        {
          id: 'backend-frontend-debug',
          name: '后端开发者 - 前端调试但不查看源码',
          pathPattern: '/src/{pages,components}/**',
          actions: ['view', 'run', 'debug'],
          deniedActions: ['source:view', 'source:edit', 'source:export'],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
      ],

      'fullstack-dev': [
        {
          id: 'fullstack-all',
          name: '全栈开发者 - 完全访问',
          pathPattern: '/**',
          actions: [
            'view',
            'source:view',
            'source:edit',
            'create',
            'update',
            'delete',
            'move',
            'copy',
            'run',
            'debug',
          ],
          priority: 800,
          createdBy: 0,
          createdAt: now,
        },
      ],

      tester: [
        {
          id: 'tester-test-files',
          name: '测试工程师 - 测试文件完全访问',
          pathPattern: '**/*.{test,spec}.{ts,tsx,js,jsx}',
          actions: [
            'view',
            'source:view',
            'source:edit',
            'create',
            'update',
            'delete',
          ],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
        {
          id: 'tester-test-dir',
          name: '测试工程师 - 测试目录完全访问',
          pathPattern: '/src/__tests__/**',
          actions: [
            'view',
            'source:view',
            'source:edit',
            'create',
            'update',
            'delete',
          ],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
        {
          id: 'tester-others-readonly',
          name: '测试工程师 - 其他文件只读',
          pathPattern: '/**',
          actions: ['view', 'source:view', 'run', 'debug'],
          deniedActions: ['source:edit', 'create', 'update', 'delete'],
          priority: 50,
          createdBy: 0,
          createdAt: now,
        },
      ],

      designer: [
        {
          id: 'designer-styles',
          name: 'UI设计师 - 样式文件完全访问',
          pathPattern: '**/*.{css,scss,less,sass}',
          actions: [
            'view',
            'source:view',
            'source:edit',
            'create',
            'update',
            'delete',
          ],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
        {
          id: 'designer-assets',
          name: 'UI设计师 - 资源文件完全访问',
          pathPattern: '/src/assets/**',
          actions: [
            'view',
            'source:view',
            'source:edit',
            'create',
            'update',
            'delete',
          ],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
        {
          id: 'designer-others-preview',
          name: 'UI设计师 - 其他文件只能预览',
          pathPattern: '/**',
          actions: ['view', 'run'],
          deniedActions: ['source:view', 'source:edit'],
          priority: 50,
          createdBy: 0,
          createdAt: now,
        },
      ],

      intern: [
        {
          id: 'intern-test-files',
          name: '实习生 - 测试文件可编辑',
          pathPattern: '**/*.{test,spec}.{ts,tsx,js,jsx}',
          actions: ['view', 'source:view', 'source:edit'],
          priority: 100,
          createdBy: 0,
          createdAt: now,
        },
        {
          id: 'intern-others-readonly',
          name: '实习生 - 其他文件只读',
          pathPattern: '/**',
          actions: ['view', 'source:view', 'run'],
          deniedActions: ['source:edit', 'create', 'update', 'delete'],
          priority: 50,
          createdBy: 0,
          createdAt: now,
        },
      ],

      guest: [
        {
          id: 'guest-readonly',
          name: '访客 - 只读',
          pathPattern: '/**',
          actions: ['view', 'run'],
          deniedActions: [
            'source:view',
            'source:edit',
            'create',
            'update',
            'delete',
          ],
          priority: 50,
          createdBy: 0,
          createdAt: now,
        },
      ],

      custom: [], // 自定义角色使用 member.policies
    };

    return ROLE_POLICIES[role] || ROLE_POLICIES['guest'];
  }

  /**
   * 路径匹配（支持 glob 模式）
   */
  private matchPath(filePath: string, pattern: string): boolean {
    return minimatch(filePath, pattern, {
      dot: true, // 匹配以 . 开头的文件
      matchBase: false,
    });
  }

  /**
   * 条件检查
   */
  private checkConditions(conditions: PermissionPolicy['conditions']): boolean {
    const now = Date.now();

    // 时间范围检查
    if (conditions.timeRange) {
      if (now < conditions.timeRange.start || now > conditions.timeRange.end) {
        return false;
      }
    }

    // 环境检查
    if (conditions.environment) {
      const currentEnv = process.env.NODE_ENV || 'development';
      if (!conditions.environment.includes(currentEnv)) {
        return false;
      }
    }

    // IP白名单检查（暂时跳过，需要从请求上下文获取IP）
    // if (conditions.ipWhitelist) {
    //   const clientIp = this.getClientIp();
    //   if (!conditions.ipWhitelist.includes(clientIp)) {
    //     return false;
    //   }
    // }

    return true;
  }

  /**
   * 获取项目基础信息
   */
  async getProjectInfo(projectId: string): Promise<LcProject | null> {
    return await this.projectModel
      .findById(projectId)
      .select({
        _id: 1,
        userId: 1,
        teamId: 1,
        member: 1,
        title: 1,
      })
      .lean();
  }

  /**
   * 更新项目成员权限
   */
  async updateProjectMember(
    projectId: string,
    userId: number,
    updates: {
      role?: ProjectRole;
      policies?: PermissionPolicy[];
      documentPermission?: DocPermission;
    },
    updatedBy: number,
  ): Promise<boolean> {
    const project = await this.projectModel.findById(projectId);
    if (!project) {
      throw new Error('项目不存在');
    }

    const memberIndex = project.member?.findIndex((m) => m.userId === userId);
    if (memberIndex === -1 || memberIndex === undefined) {
      throw new Error('用户不是项目成员');
    }

    // 更新成员信息
    const updateData: any = {};
    if (updates.role !== undefined) {
      updateData['member.$.role'] = updates.role;
    }
    if (updates.policies !== undefined) {
      updateData['member.$.policies'] = updates.policies;
    }
    if (updates.documentPermission !== undefined) {
      updateData['member.$.documentPermission'] = updates.documentPermission;
    }

    const result = await this.projectModel.updateOne(
      { _id: projectId, 'member.userId': userId },
      { $set: updateData },
    );

    return result.modifiedCount > 0;
  }

  /**
   * 添加项目成员
   */
  async addProjectMember(
    projectId: string,
    userId: number,
    memberData: {
      role?: ProjectRole;
      policies?: PermissionPolicy[];
      documentPermission?: DocPermission;
      inviteBy?: number;
    },
  ): Promise<boolean> {
    const project = await this.projectModel.findById(projectId);
    if (!project) {
      throw new Error('项目不存在');
    }

    // 检查用户是否已经是成员
    const existingMember = project.member?.find((m) => m.userId === userId);
    if (existingMember) {
      throw new Error('用户已经是项目成员');
    }

    const newMember: LcProjectMember = {
      userId,
      inviteBy: memberData.inviteBy,
      inviteAt: Date.now(),
      role: memberData.role || 'guest',
      documentPermission: memberData.documentPermission || 'view',
      policies: memberData.policies || [],
    };

    const result = await this.projectModel.updateOne(
      { _id: projectId },
      { $push: { member: newMember } },
    );

    return result.modifiedCount > 0;
  }

  /**
   * 移除项目成员
   */
  async removeProjectMember(
    projectId: string,
    userId: number,
  ): Promise<boolean> {
    const result = await this.projectModel.updateOne(
      { _id: projectId },
      { $pull: { member: { userId } } },
    );

    return result.modifiedCount > 0;
  }

  /**
   * 获取项目所有成员
   */
  async getProjectMembers(projectId: string): Promise<LcProjectMember[]> {
    const project = await this.projectModel
      .findById(projectId)
      .select({
        member: 1,
      })
      .lean();

    return project?.member || [];
  }
}
